﻿module poseidon.util.File;

private import dwt.all;
private import dwt.internal.converter;

class FileReader{
	
	private import std.stream;
	
	/**
	 * pass the buffer to receive file content, the return value indicate whether 
	 * the operation successful.
	 */
	public static bool read(char[] filename, out char[] buffer, out int bom){
	
		// reset receiver buffer
		buffer = null;
		
		File file;
		EndianStream f;
		bom = -1;
		
		try{
			file = new File(filename, FileMode.In);		
		}catch(Exception e){
			Util.trace(e.toString());
			throw e;
		}
		
		if(file.size() == 0){
			file.close();
			return true;
		}

		try
		{
			size_t i = 0;
			
			f = new EndianStream(file);
			bom = f.readBOM();
			
			// Util.trace("file length %d, bom %d", file.size(), bom);
			
			switch(bom)
			{
				case -1:
					// ANSI/MBCS should be convert to UTF8 encoding
					scope char[] buf = new char[cast(int)f.size()];
					while(!f.eof())
					{
						buf[i++] = f.getc();
					}
					/**
					 * std.system.OS conflict with dwt.internal.win32.os.OS,
					 * use full package name
					 */
					int cp = dwt.internal.win32.os.OS.GetACP();
					char[] temp = Converter.MBCSzToStr(buf.ptr, buf.length, cp); 
					buffer = temp;
					break;
				// UTF8
				case BOM.UTF8:
					// some time you get -1, set it to UTF8
					scope char[] buf = new char[cast(int)f.size()];
					while(!f.eof())
					{
						buf[i++] = f.getc();
					}
					buffer = buf[0 .. i];
					break;
				// UTF16
				case BOM.UTF16LE, BOM.UTF16BE:
					scope wchar[] wbuf = new wchar[cast(int)f.size() / wchar.sizeof];
					while(!f.eof())
					{
						wbuf[i++] = f.getcw();
					}
					buffer = std.utf.toUTF8(wbuf[0 .. i]);
					break;
				// UFT32
				case BOM.UTF32LE, BOM.UTF32BE:
					scope dchar[] dbuf = new dchar[cast(int)f.size() / dchar.sizeof];
					while(!f.eof())
					{
						dchar dch;
						f.read(dch);
						dbuf[i++] = dch;
					}
					buffer = std.utf.toUTF8(dbuf[0 .. i]);
					break;
				default : break;
			}
		}catch(Exception e){
			throw e;
		}
		finally
		{
			delete f;
			file.close();
			delete file;
		}
		
		return true;
	}
}

class FileSaver{

	private import std.stream;
	private import std.system;
	

	public this(){
	}
	/**
	 * the input buffer is in UTF8 format
	 */	
	public static bool save(char[] filename, char[] buffer, int bom = BOM.UTF8){
		int sz;
		Endian endian;
		
		// Util.trace("buffer length %d, bom %d", buffer.length, bom);
		
		switch(bom)
		{
			case -1:
				sz = char.sizeof;
				break;
			case BOM.UTF8:
				sz = char.sizeof;
				endian = std.system.endian;
				break;
			
			case BOM.UTF16LE:
				sz = wchar.sizeof;
				endian = Endian.LittleEndian;
				break;
			
			case BOM.UTF16BE:
				sz = wchar.sizeof;
				endian = Endian.BigEndian;
				break;
			
			case BOM.UTF32LE:
				sz = dchar.sizeof;
				endian = Endian.LittleEndian;
				break;
			
			case BOM.UTF32BE:
				sz = dchar.sizeof;
				endian = Endian.BigEndian;
				break;
			default : break;
		}
		
		File _f;
		EndianStream f;
		
		
		try
		{	
			// incase the file is read only
			_f = new File(filename, FileMode.OutNew);
			f = new EndianStream(_f, endian);
			if(bom != -1)
				f.writeBOM(cast(BOM)bom);
			
			switch(sz)
			{
				case char.sizeof:
					if(bom == -1){
						// UTF8 ot ANSI/MBCS
						int cp = dwt.internal.win32.os.OS.GetACP();
						char[] cs = Converter.StrToMBCS(buffer, cp);
						f.writeExact(cs.ptr, cs.length);
					}else{
						// UTF8
						f.writeExact(buffer.ptr, buffer.length);
					}
					break;
				
				case wchar.sizeof:
					foreach(wchar ch; buffer)
					{	
						f.write(ch);
					}
					break;
				
				case dchar.sizeof:
					foreach(dchar ch; buffer)
					{
						f.write(ch);
					}
					break;
				default : break;
			}
		}catch(Exception e){
			Util.trace(e.toString());
			throw e;
		}
		finally
		{
			delete f;
			_f.close();
			delete _f;
		}
		
		return true;
	}
}